/*
 是在应用中完成特定任务的，互相分离的 功能组
 可以把函数理解为有名字的闭包
 */

import Cocoa

let volunteerCounts = [1,3,40,32,2,53,77,13]
func sortAscending(_ i:Int, _ j:Int) -> Bool
{
    return i<j; //升序     return i>j降序
}
let volunteersSorted = volunteerCounts.sorted(by: sortAscending)

/*
 首先，我们创建了函数sortAscending（_:_:）。这个函数比较两个整数，并返回一个布尔值，用来表示整数i是否应该在整数j前面。
 这个全局函数是一个命名闭包（所有函数都是闭包）,因此可以将其作为sorted(by:)的参数。
 实际上，上面的写法还不够简洁，闭包的一般形式如下：
 {(parameters) -> return type in}
 */

let volunteersSorted1 = volunteerCounts.sorted(by: {
    (i:Int,j:Int) -> Bool in
    return i<j
})

//移除两个参数类型（可以使用类型推断获得），移除了返回值的类型信息（编译器知道），移除return是因为这里只有一个表达式，编译器也知道，多个就要加上了。
let volunteersSorted2 = volunteerCounts.sorted(by: {i,j in i < j})


//快捷参数命名：类型和值都一样。编译器的类型推断能力让它知道闭包接受的参数个数和类型，这意味着不需要给参数命名。
let volunteersSorted3 = volunteerCounts.sorted(by: {$0 < $1})
//编译器知道sorted接收一个闭包，这个闭包接收两个参数，他们的类型和数组参数中的类型是一样的。比较闭包的两个参数，可以用$0表示第一个参数，$1表示第二个


//2.函数作为返回值
func makeTownGrand() -> (Int,Int)->Int
{
    func buildRoads(byAddingLights lights:Int,toExistingLights existingLishts:Int)->Int
    {
        return lights + existingLishts
    }
    return buildRoads
}
var stopLights = 4
let townPlanByAddingLightsToExistingLights = makeTownGrand()
stopLights = townPlanByAddingLightsToExistingLights(4,stopLights)
print("Knowhere has \(stopLights) stopLights")

//3.函数作为参数
func makeTownGrand1(WithBudget budget:Int,condition:(Int)->Bool)->((Int,Int)->Int)?
{
    if condition(budget)
    {
        func buildRoads(byAddingLights lights:Int,toExistingLights existingLights:Int) -> Int
        {
            return lights + existingLights;
        }
        return buildRoads
    }
    else
    {
        return nil
    }
}
func evaluate(budget:Int) -> Bool
{
    return budget > 10000
}

var stopLights1 = 4
if let townPlanByAddingLightsToExistingLights1 = makeTownGrand1(WithBudget: 1000, condition: evaluate)
{
    stopLights1 = townPlanByAddingLightsToExistingLights1(4,stopLights1)
}

//4.闭包可以捕获变量
//闭包和函数能记录在其闭合作用去中定义的变量所封装的内部信息。
func makePopulationTracker(forInitialPopulation population:Int) -> (Int)->Int
{
    var totalPopulation = population
    func populationTracker(growth:Int)->Int
    {
        totalPopulation += growth
        return totalPopulation
    }
    return populationTracker
}
var currentPopulation = 5422
let growby = makePopulationTracker(forInitialPopulation: currentPopulation)
growby(500)
growby(500)
//currentPopulation = growby(500)


//5.闭包是引用类型
//这意味着当你把函数赋给常量或变量时，实际上是在让这个常量或变量指向这个函数。我们并没有为这个函数创建新的副本。
//这个事实将导致另外一个重要的结果，就是，如果通过新的常量或变量调用这个函数，那么其作用于不活的任何信息都会改变。

let newGrowby = growby;
newGrowby(500)
let newGrowby1 = makePopulationTracker(forInitialPopulation: currentPopulation)
newGrowby1(500)


//6.函数式编程
